﻿/* 
 * RAP 
 * 
 * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
 * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED 
 * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
 * 
 * Copyright (C) 2008 Phill Tew. All rights reserved.
 * 
 */

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Packaging;
using System.Windows.Forms;
using Rap.Packing;
using Rug.Cmd;
using Rug.Cmd.Colors;

namespace Rap
{
    class Program
	{
		#region Main
		
		static void Main(string[] args)
        {			
            ConsoleColorState state = RC.ColorState;

			RC.Theme = ConsoleColorTheme.Load(ConsoleColorDefaultThemes.Colorful);

			#region Setup Argument Parser
			
			string Title = Strings.App_Title + " " + typeof(Program).Assembly.GetName().Version.ToString();

			ArgumentParser parser = new ArgumentParser("Rap", Title + Environment.NewLine + Strings.App_AboutText);

            parser.AboutTitleText = Title;
			parser.AboutText = Strings.App_AboutText;
			parser.AboutTextLong = Strings.App_AboutTextLong;
			parser.CreditsText = Strings.App_CreditsText;
			parser.LegalText = Strings.App_LegalText;
			parser.HasApplicationDocument = true;                    
			
			StringArgument PathString = new StringArgument(Strings.Arg_Path_OptionName, Strings.Arg_Path_ShortHelp, Strings.Arg_Path_Help);
			StringListArgument FileList = new StringListArgument(Strings.Arg_FileList_OptionName, Strings.Arg_FileList_ShortHelp, Strings.Arg_FileList_Help);

			BoolSwitch PackSwitch = new BoolSwitch(Strings.Arg_Pack_ShortHelp, Strings.Arg_Pack_Help);
			BoolSwitch UnpackSwitch = new BoolSwitch(Strings.Arg_Unpack_ShortHelp, Strings.Arg_Unpack_Help);
			StringArgument ExtractSwitch = new StringArgument(Strings.Arg_Extract_OptionName, Strings.Arg_Extract_ShortHelp, Strings.Arg_Extract_Help);
          
            parser.FirstArgument = PathString; 
            parser.DefaultArgument = FileList; 

			parser.Add("/", Strings.Arg_Pack_Key, PackSwitch);
			parser.Add("/", Strings.Arg_Unpack_Key, UnpackSwitch);
			parser.Add("/", Strings.Arg_Extract_Key, ExtractSwitch);

			BoolSwitch BuildSwitch = new BoolSwitch(Strings.Arg_BuildSwitch_ShortHelp, Strings.Arg_BuildSwitch_Help);
			BoolSwitch QuietSwitch = new BoolSwitch(Strings.Arg_QuietSwitch_ShortHelp, Strings.Arg_QuietSwitch_Help);
			EnumSwitch VerboseSwitch = new EnumSwitch(Strings.Arg_Verbose_ShortHelp, Strings.Arg_Verbose_Help, typeof(ConsoleVerbosity));

			parser.Add("/", Strings.Arg_BuildSwitch_Key, BuildSwitch);
			parser.Add("/", Strings.Arg_QuietSwitch_Key, QuietSwitch);
			parser.Add("/", Strings.Arg_Verbose_Key, VerboseSwitch);

			PlusMinusSwitch YesTooAll = new PlusMinusSwitch(Strings.Arg_YesTooAll_ShortHelp, Strings.Arg_YesTooAll_Help, false);

			parser.Add(new string(ConsoleChars.GetMathsChar(ConsoleMathsChars.PlusMinus), 1), Strings.Arg_YesTooAll_Key, YesTooAll);

			#endregion
			
			try
            {
                parser.Parse(args);

                if (!parser.HelpMode)
                {
					RC.IsBuildMode = BuildSwitch.Defined;

                    if (QuietSwitch.Defined)
                    {
                        RC.Verbosity = ConsoleVerbosity.Quiet;
                        RC.UseDefaultPromptAnswer = true;
                        RC.DefaultPromptAnswer = true; 
                    }

					if (VerboseSwitch.Defined)
					{
						RC.Verbosity = (ConsoleVerbosity)VerboseSwitch.Value;
					}                    
                    
                    if (YesTooAll.Defined)
                    {
                        RC.UseDefaultPromptAnswer = true;
                        RC.DefaultPromptAnswer = YesTooAll.Value; 
                    }

                    RC.WriteLine(ConsoleVerbosity.Normal, "");
                    RC.WriteLine(ConsoleVerbosity.Minimal, ConsoleThemeColor.TitleText, Title);
                    
                    List<string> fileList = FileList.Value as List<string>;

                    RC.ForegroundThemeColor = ConsoleThemeColor.Text;

                    if (Helper.IsNullOrEmpty(PathString.Value))
                    {
						RC.WriteError(201, Strings.Error_ArchiveFileMustBeSupplyed);
                        return;
                    }

                    string archive = PathString.Value;

                    FileInfo info = new FileInfo(archive);

                    if (PackSwitch.Defined)
					{
						#region Pack
						
						CmdHelper.WriteSimpleBanner(Strings.Packer_PackArchive, ' ', RC.Theme[ConsoleThemeColor.AppBackground], RC.Theme[ConsoleThemeColor.Text]);

                        RC.WriteLine(ConsoleThemeColor.SubText2, " " + PathString.Value);

						foreach (string dFile in fileList)
						{ 
                            RC.WriteLine(ConsoleThemeColor.SubText3, " " + dFile);
						}

                        if (info.Exists)
                        {
                            bool result = false;

                            if (RC.UseDefaultPromptAnswer)
                                result = RC.DefaultPromptAnswer;
                            else
                            {
								RC.WritePrompt(Strings.Prompt_ArchiveAllreadyExists);

                                if (!RC.IsBuildMode)
                                {
                                    bool hasAnswer = false;

                                    while (!hasAnswer)
                                    {
                                        ConsoleKeyInfo key = RC.ReadKey(true);
                                        string ynChar = char.ToLower(key.KeyChar).ToString();

										if (ynChar == Strings.Prompt_ArchiveAllreadyExists_YesChar)
                                        {
                                            hasAnswer = true;
                                            result = true;
                                        }
										else if (ynChar == Strings.Prompt_ArchiveAllreadyExists_NoChar)
                                        {
                                            hasAnswer = true;
                                            result = false;
                                        }
                                        else
                                        {
											RC.WritePrompt(Strings.Prompt_ArchiveAllreadyExists_Retry);
                                        }
                                    }
                                }                                
                            }

                            if (result)
                            {
                                info.Delete(); 
                            }
                            else
                            {
								RC.WriteLine(ConsoleVerbosity.Minimal, Strings.Packer_Exit); 
                                return;
                            }
                        }

                        PackPackage(info.FullName, fileList.ToArray());

						#endregion
					}
                    else if (UnpackSwitch.Defined)
					{
						#region Unpack To Current Folder
						
						CmdHelper.WriteSimpleBanner(Strings.Unpack_UnpackArchive, ' ', RC.Theme[ConsoleThemeColor.AppBackground], RC.Theme[ConsoleThemeColor.Text]);

                        if (!info.Exists)
                        {
                            RC.WriteError(202, string.Format(Strings.Error_TheArchiveDoesNotExist, archive));
                            return;
                        }
                        else
                        {
                            RC.WriteLine(ConsoleThemeColor.SubText2, " " + PathString.Value);
                        }

                        UnpackPackage(info.FullName, Environment.CurrentDirectory);
                        RC.WriteLine("");

						#endregion
					}
                    else if (ExtractSwitch.Defined)
					{
						#region Extract To Folder
						
						CmdHelper.WriteSimpleBanner(Strings.Extract_ExtractToFolder, ' ', RC.Theme[ConsoleThemeColor.AppBackground], RC.Theme[ConsoleThemeColor.Text]);

                        if (!info.Exists)
                        {
							RC.WriteError(203, string.Format(Strings.Error_TheArchiveDoesNotExist, archive));
                            return;
                        }
                        else
                        {
                            RC.WriteLine(ConsoleThemeColor.SubText2, " " + PathString.Value);
                        }

                        DirectoryInfo folder = new DirectoryInfo(ExtractSwitch.Value);

                        if (!folder.Exists)
                        {
                            RC.WriteError(204, string.Format(Strings.Error_TheFolderDoesNotExist, folder.FullName));
                            return;
                        }
                        else
                        {
                            RC.WriteLine(ConsoleThemeColor.Text1, " " + folder.FullName); 
                        }

                        UnpackPackage(info.FullName, folder.FullName);
                        RC.WriteLine("");

						#endregion
					}
                }
            }
            catch (Exception ex)
            {
                RC.WriteException(299, ex);
                RC.WriteLine("");                
            }
            finally
            {		

#if DEBUG
				RC.ReadKey(true); 
#endif
                RC.ColorState = state;
            }             
        }

		#endregion

		#region Pack

		private static void PackPackage(string archive, string[] filelist)
        {
            Package package = null;

            bool compleate = false;

			long totalUnpackedSize = 0; 

            try
            {
                package = PackageHelper.GetPackage(archive, true, FileAccess.ReadWrite);

				foreach (string filepath in filelist)
				{
					ProcessFilePath(package, filepath, ref totalUnpackedSize);
				}

                compleate = true;                
            }
            finally
            {
                if (package != null)
                {
                    PackageHelper.ReleasePackage(package);

                    if (compleate)
                    {
						string path2 = Application.UserAppDataPath + Path.DirectorySeparatorChar + Guid.NewGuid() + ".zip";

                        Recompressor.RecompressPackage(archive, path2, true);

                        File.Delete(archive);

                        File.Move(path2, archive);

						long packedSize = new FileInfo(archive).Length;
						double compression = (100 - (((double)packedSize / (double)totalUnpackedSize) * 100.0));

						ConsoleThemeColor resultColour = ConsoleThemeColor.Text;

						if (packedSize <= (totalUnpackedSize / 4) * 3)
							resultColour = ConsoleThemeColor.TextGood;
						else if (packedSize < totalUnpackedSize)
							resultColour = ConsoleThemeColor.SubTextNutral;
						else
							resultColour = ConsoleThemeColor.TextBad;

						RC.WriteLine("");
						CmdHelper.WriteInfoToConsole(ConsoleVerbosity.Minimal, Strings.Pack_UnpackedSize, CmdHelper.GetMemStringFromBytes(totalUnpackedSize, true), RC.Theme[ConsoleThemeColor.Text]);
						CmdHelper.WriteInfoToConsole(ConsoleVerbosity.Minimal, Strings.Pack_PackedSize, CmdHelper.GetMemStringFromBytes(packedSize, true), RC.Theme[resultColour]);
						CmdHelper.WriteInfoToConsole(ConsoleVerbosity.Minimal, Strings.Pack_Compression, compression.ToString("N2") + "%  ", RC.Theme[ConsoleThemeColor.SubTextGood]);						

                        if (RC.CanManipulateBuffer && (int)RC.Verbosity >= (int)ConsoleVerbosity.Normal)
                        {
                            RC.WriteLine("");
                            Rug.Cmd.CmdHelper.WriteRuglandLogo(RC.BufferWidth - 21, RC.CursorTop - 4, 2, 1, false, ConsoleShade.Opaque, false, true);							
                        }
                    }
                }
            }
        }

		#region Process File Path
		
		private static void ProcessFilePath(Package package, string filepath, ref long totalUnpackedSize)
        {
            string currentFile = null; 

            if (filepath.Contains("*"))
            {
                // contains a wild card
				string folderPath = filepath.Substring(0, filepath.LastIndexOf(Path.DirectorySeparatorChar) + 1);

				string fileName = filepath.Substring(filepath.LastIndexOf(Path.DirectorySeparatorChar) + 1);

				try
				{
					foreach (string file in Directory.GetFiles(folderPath, fileName))
					{
						totalUnpackedSize += new FileInfo(file).Length; 

						string uri = "/" + file.Substring(file.LastIndexOf(Path.DirectorySeparatorChar) + 1);

						currentFile = GetConsolePathString(uri);

						RC.Write(ConsoleThemeColor.TitleText, currentFile);						

						PackageHelper.AddFileToPackage(package, uri, file);

						if (RC.CanManipulateBuffer)
						{
							RC.CursorLeft = 0;
							RC.Write(ConsoleThemeColor.Text, currentFile);
						}

						RC.Write(ConsoleThemeColor.SubText, GetConsoleRemainderString(currentFile));
						RC.WriteLine(ConsoleThemeColor.SubTextGood, " " + Strings.Text_OK);
					}
				}
				catch (Exception ex)
				{
					if (currentFile != null && RC.CanManipulateBuffer)
					{
						RC.CursorLeft = 0;
						RC.WriteLine(ConsoleThemeColor.ErrorColor1, currentFile.PadRight(RC.BufferWidth, ' '));
					}

					RC.WriteException(205, ex);

					throw ex;
				}
            }
            else
            {
				if (filepath.EndsWith(Path.DirectorySeparatorChar.ToString()))
                {
                    DirectoryInfo dirInfo = new DirectoryInfo(filepath);

                    AddFolderToPackage(package, "/" + dirInfo.Name + "/", filepath, ref totalUnpackedSize);
                }
                else
                {
                    try 
                    {
						string fileName = "/" + filepath.Substring(filepath.LastIndexOf(Path.DirectorySeparatorChar) + 1);

                        currentFile = GetConsolePathString(fileName);

                        if (!File.Exists(filepath))
                        {
                            RC.WriteError(206, string.Format(Strings.Error_FileDoesNotExist, filepath));
                        }
                        else
                        {
							totalUnpackedSize += new FileInfo(filepath).Length; 

                            RC.Write(ConsoleThemeColor.TitleText, currentFile);

                            PackageHelper.AddFileToPackage(package, fileName, filepath);

                            if (RC.CanManipulateBuffer)
                            {
                                RC.CursorLeft = 0;
                                RC.Write(ConsoleThemeColor.Text, currentFile);
                            }

                            RC.Write(ConsoleThemeColor.SubText, GetConsoleRemainderString(currentFile));
							RC.WriteLine(ConsoleThemeColor.SubTextGood, " " + Strings.Text_OK);   
                        }
                    }
                    catch (Exception ex)
                    {
                        if (currentFile != null && RC.CanManipulateBuffer)
                        {
                            RC.CursorLeft = 0;
                            RC.WriteLine(ConsoleThemeColor.ErrorColor1, currentFile.PadRight(RC.BufferWidth, ' '));
                        }

                        RC.WriteException(207, ex);

                        throw ex; 
                    }
                }
            }
        }

		#endregion

		#region Add Folder To Package

		private static void AddFolderToPackage(Package package, string path, string folderPath, ref long totalUnpackedSize)
        {
            string currentFile = null; 

            foreach (string dir in Directory.GetDirectories(folderPath))
            {
                DirectoryInfo dirInfo = new DirectoryInfo(dir);

                AddFolderToPackage(package, path + dirInfo.Name + "/", dir, ref totalUnpackedSize);
            }

            foreach (string file in Directory.GetFiles(folderPath))
            {
                FileInfo info = new FileInfo(file);
				
				totalUnpackedSize += info.Length; 

                string fileName = path + info.Name;

                currentFile = GetConsolePathString(fileName);

                RC.Write(ConsoleThemeColor.TitleText, currentFile);

                PackageHelper.AddFileToPackage(package, fileName, file);

                if (RC.CanManipulateBuffer)
                {
                    RC.CursorLeft = 0;
                    RC.Write(ConsoleThemeColor.Text, currentFile);
                }

                RC.Write(ConsoleThemeColor.SubText, GetConsoleRemainderString(currentFile));
				RC.WriteLine(ConsoleThemeColor.SubTextGood, " " + Strings.Text_OK);   
            }
        }

		#endregion

		#endregion

		#region Unpack

		private static void UnpackPackage(string archive, string destination)
        {
            Package package = null;

            string currentFile = null; 

            try
            {
                package = PackageHelper.GetPackage(archive, false, FileAccess.Read);

                foreach (PackagePart part in package.GetParts())
                {
                    string path = part.Uri.ToString();

                    currentFile = GetConsolePathString(path);

                    RC.Write(ConsoleThemeColor.TitleText, currentFile);
                    
                    using (Stream stream = part.GetStream(FileMode.Open, FileAccess.Read))
                    {
						string newPath = destination + path.Replace('/', Path.DirectorySeparatorChar);

                        FileInfo newFileInfo = new FileInfo(newPath);

						if (!newFileInfo.Directory.Exists)
						{
							newFileInfo.Directory.Create();
						}

                        using (FileStream newFile = new FileStream(newPath, FileMode.Create, FileAccess.Write))
                        {
                            PackageHelper.CopyStream(stream, newFile);

                            newFile.Flush();
                            newFile.Close();
                        }
                    }

                    if (RC.CanManipulateBuffer)
                    {
                        RC.CursorLeft = 0;
                        RC.Write(ConsoleThemeColor.Text, currentFile);
                    }

                    RC.Write(ConsoleThemeColor.SubText, GetConsoleRemainderString(currentFile));
					RC.WriteLine(ConsoleThemeColor.SubTextGood, " " + Strings.Text_OK);                    
                }

				RC.WriteLine(ConsoleThemeColor.SubTextGood, Strings.Text_Done);
            }
            catch (Exception ex)
            {
                if (currentFile != null && RC.CanManipulateBuffer)
                {
                    RC.CursorLeft = 0;
                    RC.WriteLine(ConsoleThemeColor.ErrorColor1, currentFile.PadRight(RC.BufferWidth, ' '));
                }

                RC.WriteException(208, ex);

                throw ex; 
            }
            finally
            {
				if (package != null)
				{
					PackageHelper.ReleasePackage(package);
				}
            }
        }

		#endregion

		#region Console Info Helpers

		private static string GetConsolePathString(string path)
		{
			string currentFile = path;

			if (currentFile.Length >= RC.BufferWidth - 6)
			{
				currentFile = currentFile.Substring(0, RC.BufferWidth - 6);
			}

			return " " + currentFile;
		}

		private static string GetConsoleRemainderString(string path)
		{
			return new string('.', (RC.BufferWidth - 6) - path.Length);
		}

		#endregion

    }
}
